package mgox

import (
	"context"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/minglic/mongodb-orm/pagerx"
	"github.com/tidwall/gjson"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"go.uber.org/zap"
	"sxk.games/common/v4/logs"
	"sxk.games/common/v4/utils"
)

var conf *Config
var logger *zap.Logger

type MongoClient struct {
	clientPrimary  *mongo.Client
	clientReadonly *mongo.Client
}

var MgoxClient = &MongoClient{}

func SetPrimaryClient(client *mongo.Client) {
	MgoxClient.clientPrimary = client
}

func SetReadonlyClient(client *mongo.Client) {
	MgoxClient.clientReadonly = client
}

type Config struct {
	DefaultDatabaseName string //默认数据库名称
}

type MongoDB struct {
	Client     *mongo.Client
	Database   *mongo.Database
	Collection *mongo.Collection
	sigResult  *mongo.SingleResult
	cursor     *mongo.Cursor
	model      any
	err        error
	pager      *pagerx.Pager
	start      *pagerx.Start
	filter     any
	fields     bson.M
	limit      *int64
	skip       *int64
	timeout    *time.Duration
	suffix     *string
	update     bson.M
	collection string
	showLog    bool
}

func SetLogger(logger_ *zap.Logger) {
	logger = logger_
}

// 连接主节点
func ConnectPrimary(callback func(clientOptions *options.ClientOptions, conf *Config)) {
	SetPrimaryClient(Connect(callback))
}

// 连接只读节点
func ConnectReadonly(callback func(clientOptions *options.ClientOptions, conf *Config)) {
	SetReadonlyClient(Connect(callback))
}

// 连接到 MongoDB
func Connect(callback func(clientOptions *options.ClientOptions, conf *Config)) *mongo.Client {

	clientOptions := options.Client()

	if conf == nil {
		conf = &Config{}
	}

	callback(clientOptions, conf)

	//执行连接
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	client, err := mongo.Connect(ctx, clientOptions)
	if err != nil {
		panic(err)
	}

	logs.DebugM("连接地址：", clientOptions.GetURI())

	//测试连接
	ctx2, cancel2 := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel2()
	err = client.Ping(ctx2, nil)
	if err != nil {
		panic(err)
	}

	return client
}

func GetPrimaryClient() *mongo.Client {
	return MgoxClient.clientPrimary
}

func GetReadonlyClient() *mongo.Client {
	return MgoxClient.clientReadonly
}

// 获取读写节点
func GetDB(database ...string) *MongoDB {
	return getdb(MgoxClient.clientPrimary, database...)
}

// 获取只读节点，如果只读节点未连接，则返回主节点
func GetDBR(database ...string) *MongoDB {
	if MgoxClient.clientReadonly == nil {
		return GetDB(database...)
	}
	return getdb(MgoxClient.clientPrimary, database...)
}

func getdb(client *mongo.Client, database ...string) *MongoDB {
	db := &MongoDB{
		Client: client,
	}
	if len(database) > 0 {
		db.Database = client.Database(database[0])
	} else {
		if conf.DefaultDatabaseName != "" {
			db.Database = client.Database(conf.DefaultDatabaseName)
		} else {
			panic(fmt.Errorf("未设置默认数据库名"))
		}
	}
	return db
}

func (db *MongoDB) GetDatabase() *mongo.Database {
	return db.Database
}

/**
 * @Author: minglic
 * @description: 设置单次请求的过期时间
 * @param {time.Duration} timeout
 * @return {*}
 */
func (db *MongoDB) Timeout(timeout time.Duration) *mongo.Database {
	db.timeout = &timeout
	return db.Database
}

/**
 * @Author: minglic
 * @description: 实例化Collection
 * @param {string} collection
 * @return {*}
 */
func (db *MongoDB) Coll(collection string) *MongoDB {
	db.Collection = db.Database.Collection(collection)
	return db
}

func (db *MongoDB) GetFilter() any {
	return db.filter
}

/**
 * @Author: minglic
 * @description: 实例化Collection
 * @param {string} collection
 * @return {*}
 */
func (db *MongoDB) Model(model any) *MongoDB {
	db.model = model

	db.collection, db.err = utils.GetEntityCollName(model)
	if db.err != nil {
		return db
	}

	if db.suffix != nil {
		db.collection += "_" + *db.suffix
	}

	return db.Coll(db.collection)
}

/**
 * @Author: minglic
 * @description: 给集合名加后缀
 * @param {string} suffix
 * @return {*}
 */
func (db *MongoDB) Suffix(suffix string) *MongoDB {
	db.suffix = &suffix
	return db
}

func (db *MongoDB) context() (context.Context, context.CancelFunc) {
	if db.timeout != nil {
		return context.WithTimeout(context.Background(), time.Duration(*db.timeout)*time.Second)
	} else {
		return context.TODO(), func() {}
	}
}

/**
 * @Author: minglic
 * @description: 插入文档
 * @param {any} document
 * @return {*}
 */
func (db *MongoDB) InsertOne(model any) (*mongo.InsertOneResult, error) {
	ctx, cancel := db.context()
	defer cancel()

	db.PrintLog("insertOne")

	if db.model == nil {
		db.Model(model)
	}

	return db.Collection.InsertOne(ctx, model)
}

/**
 * @Author: minglic
 * @description: 插入文档
 * @param {any} document
 * @return {*}
 */
func (db *MongoDB) InsertMany(model ...any) (*mongo.InsertManyResult, error) {
	ctx, cancel := db.context()
	defer cancel()

	db.PrintLog("insertMany")

	return db.Collection.InsertMany(ctx, model)
}

// 打印查询日志
func (db *MongoDB) PrintLog(oprater string) {
	if logger != nil && db.showLog {
		logger.Debug(fmt.Sprintf("database = %s", db.Database.Name()))
		logger.Debug(fmt.Sprintf("option = %s", oprater))
		logger.Debug(fmt.Sprintf("collName = %s", db.collection))
		if db.filter != nil {
			if reflect.TypeOf(db.filter).String() == "primitive.D" {
				logger.Debug(fmt.Sprintf("filter = %s", utils.ToJson(utils.BsonD2Map(db.filter.(primitive.D)))))
			} else {
				logger.Debug(fmt.Sprintf("filter = %s", utils.ToJson(db.filter)))
			}
		}
		if db.fields != nil {
			logger.Debug(fmt.Sprintf("fields = %s", ToJson(db.fields)))
		}
		if db.update != nil {
			logger.Debug(fmt.Sprintf("update = %s", ToJson(db.update)))
		}
		if db.pager != nil {
			logger.Debug(fmt.Sprintf("pager = %s", ToJson(db.pager)))
		}
	}
}

func (db *MongoDB) ShowLog() *MongoDB {
	db.showLog = true
	return db
}

func ToJson(obj any) string {
	byte, err := json.Marshal(obj)
	if err != nil {
		return ""
	}
	return string(byte)
}

/**
 * @Author: minglic
 * @description: 聚合查询
 * @return {*}
 */
func (db *MongoDB) Pipe(pineline []bson.M) *MongoDB {
	ctx, cancel := db.context()
	defer cancel()

	db.PrintLog("Pipe")

	if db.showLog {
		for _, v := range pineline {
			if v["$match"] != nil {
				db.filter = v["$match"]
				break
			}
		}
		db.Explain()
	}

	db.cursor, db.err = db.Collection.Aggregate(ctx, pineline)
	return db
}

/**
 * @Author: minglic
 * @description: 查询单个文档
 * @return {*}
 */
func (db *MongoDB) FindOne() *MongoDB {
	ctx, cancel := db.context()
	defer cancel()

	option := options.FindOne()
	option.Projection = db.fields

	db.PrintLog("findone")

	if db.filter == nil {
		db.filter = bson.D{}
	}

	db.sigResult = db.Collection.FindOne(ctx, db.filter, option)
	return db
}

/**
 * @Author: minglic
 * @description: 将数据绑定至结构体
 * @param {any} result
 * @return {*}
 */
func (db *MongoDB) One(model ...any) error {
	if db.err != nil {
		return db.err
	}
	if len(model) > 0 {
		db.model = model[0]
	}
	//如果未执行查询，则执行一次查询
	if db.sigResult == nil {
		db.FindOne()
	}
	return db.sigResult.Decode(db.model)
}

/**
 * @Author: minglic
 * @description: 将数据绑定至结构体
 * @param {any} result
 * @return {*}
 */
func (db *MongoDB) PipeDecode(model ...any) error {
	if db.err != nil {
		return db.err
	}
	if len(model) > 0 {
		db.model = model[0]
	}
	if db.cursor == nil {
		return fmt.Errorf("未执行查询:Pipe")
	}

	defer db.cursor.Close(context.TODO())
	db.err = db.cursor.All(context.TODO(), db.model)
	return db.err
}

/**
 * @Author: minglic
 * @description: 字段选择 支持 bson.M{"field": 1} / []string{"field"} / "field"  三种写法
 * @param {bson.M} fields
 * @return {*}
 */
func (db *MongoDB) Select(fields any) *MongoDB {
	if fields == nil {
		return db
	}

	switch f := fields.(type) {
	case []string:
		if len(f) == 0 {
			return db
		}
		fs := bson.M{}
		for _, v := range f {
			if strings.HasPrefix(v, "-") {
				fs[v] = 0
			} else {
				fs[v] = 1
			}
		}
		db.fields = fs
	case bson.M:
		if len(f) == 0 {
			return db
		}
		db.fields = f
	case string:
		if f == "" {
			return db
		}
		db.fields = bson.M{f: 1}
	case map[string]int:
		if len(f) == 0 {
			return db
		}
		fm := primitive.M{}
		for k, v := range f {
			fm[k] = v
		}
		db.fields = fm
	default:

	}
	return db
}

/**
 * @Author: minglic
 * @description: 查询时排除某些字段，支持 bson.M{"field": 0} / []string{"field"} / "field"  三种写法
 * @param {any} fields
 * @return {*}
 */
func (db *MongoDB) Omit(fields any) *MongoDB {
	switch f := fields.(type) {
	case []string:
		fs := bson.M{}
		for _, v := range f {
			fs[v] = 0
		}
		db.fields = fs
	case bson.M:
		db.fields = f
	case string:
		db.fields = bson.M{f: 0}
	default:

	}
	return db
}

func (db *MongoDB) Filter(filter any) *MongoDB {
	switch f := filter.(type) {
	case primitive.ObjectID:
		db.filter = bson.D{{Key: "_id", Value: f}}
	case string:
		if oid, err := primitive.ObjectIDFromHex(f); err == nil {
			db.filter = bson.D{{Key: "_id", Value: oid}}
		} else {
			db.filter = bson.D{{Key: "_id", Value: f}}
		}
	default:
		db.filter = filter
	}

	db.Explain()

	return db
}

func (db *MongoDB) FilterID(id any) *MongoDB {

	switch f := id.(type) {
	case *primitive.ObjectID, primitive.ObjectID:
		db.filter = bson.D{{Key: "_id", Value: f}}
	case string:
		if oid, err := primitive.ObjectIDFromHex(f); err == nil {
			db.filter = bson.D{{Key: "_id", Value: oid}}
		} else {
			db.filter = bson.D{{Key: "_id", Value: f}}
		}
	case *string:
		if oid, err := primitive.ObjectIDFromHex(*f); err == nil {
			db.filter = bson.D{{Key: "_id", Value: oid}}
		} else {
			db.filter = bson.D{{Key: "_id", Value: f}}
		}
	default:
		db.err = fmt.Errorf("不支持的数据类型")
	}
	return db
}

func (db *MongoDB) FilterModel() *MongoDB {
	id, err := utils.GetFieldAny(db.model, "ID")
	if err != nil {
		db.err = err
		return db
	}

	db.filter = bson.D{{Key: "_id", Value: id}}
	return db
}

func (db *MongoDB) Pager(pager *pagerx.Pager) *MongoDB {
	if len(pager.Sort) == 0 {
		if db.pager != nil && db.pager.Sort != nil && len(db.pager.Sort) > 0 {
			pager.Sort = db.pager.Sort
		}
	}
	db.pager = pager
	db.limit = &pager.Size
	skip := pager.Skip()
	db.skip = &skip
	return db
}

func (db *MongoDB) Limit(limit int64) *MongoDB {
	db.limit = &limit
	return db
}

func (db *MongoDB) Skip(skip int64) *MongoDB {
	db.skip = &skip
	return db
}

func (db *MongoDB) Sort(sort ...string) *MongoDB {
	if db.pager == nil {
		db.pager = &pagerx.Pager{
			Sort: sort,
		}
	} else {
		db.pager.Sort = sort
	}
	return db
}

func (db *MongoDB) Count() (int64, error) {
	ctx, cancel := db.context()
	defer cancel()
	if db.filter == nil {
		db.filter = bson.D{}
	}
	return db.Collection.CountDocuments(ctx, db.filter)
}

// 查询是否有报错
func (db *MongoDB) Error() error {
	return db.err
}

/**
 * @Author: minglic
 * @description: 生成排序规则（以“-”开头代表降序，否则为升序）
 * @return {*}
 */
func (db *MongoDB) sort() *bson.D {

	if db.pager == nil || db.pager.Sort == nil || len(db.pager.Sort) == 0 {
		return &bson.D{}
	}

	sort := bson.D{}
	for _, v := range db.pager.Sort {
		if strings.HasPrefix(v, "-") {
			// sort[v[1:]] = -1
			sort = append(sort, bson.E{Key: v[1:], Value: -1})
		} else {
			// sort[v] = 1
			sort = append(sort, bson.E{Key: v, Value: 1})
		}
	}

	return &sort
}

/**
 * @Author: minglic
 * @description: 查询多条数据并分页
 * @param {bson.D} filter
 * @param {*pagerx.Pager} pager
 * @return {*}
 */
func (db *MongoDB) ListPage() *MongoDB {
	ctx, cancel := db.context()
	defer cancel()

	if db.filter == nil {
		db.filter = bson.D{}
	}

	total, err := db.Collection.CountDocuments(ctx, db.filter)
	if err != nil {
		db.err = err
		return db
	}
	db.pager.Total = &total

	ctx1, cancel1 := db.context()
	defer cancel1()

	option := options.Find()
	option.Limit = &db.pager.Size
	skip := db.pager.Skip()
	option.Skip = &skip

	option.Sort = db.sort()

	if db.fields != nil {
		option.Projection = db.fields
	}

	if db.filter == nil {
		db.filter = bson.D{}
	}

	result, err := db.Collection.Find(ctx1, db.filter, option)
	if err != nil {
		db.err = err
		return db
	}

	db.cursor = result

	db.PrintLog("listPage")

	return db
}

func (db *MongoDB) Start(start *pagerx.Start) *MongoDB {
	db.start = start
	return db
}

/**
 * @Author: minglic
 * @description: 查询多条数据并按起始ID分页
 * @param {bson.D} filter
 * @param {*pagerx.Pager} pager
 * @return {*}
 */
func (db *MongoDB) ListStart() *MongoDB {

	if db.start == nil {
		db.err = fmt.Errorf("必需提供分页参数")
		return db
	}
	if db.filter == nil {
		db.filter = bson.D{}
	}
	if db.start.Sort != 1 && db.start.Sort != -1 {
		db.err = fmt.Errorf("sort只能是1或-1")
		return db
	}

	if db.start.StartID != nil {

		objId, err := utils.ToObjectId(db.start.StartID)
		if err != nil {
			db.err = err
			return db
		}

		gtlt := "$gt"
		if db.start.Sort == -1 {
			gtlt = "$lt"
		}

		switch f := db.filter.(type) {
		case bson.M:
			// 如果 filter 是 bson.M 类型，可以直接添加查询条件
			f["_id"] = bson.M{gtlt: objId}
		case bson.D:
			// 如果 filter 是 bson.D 类型，需要先将查询条件转换为 bson.E，并添加到 bson.D 中
			element := bson.E{Key: "_id", Value: bson.M{gtlt: objId}}
			db.filter = append(bson.D{element}, db.filter.(bson.D)...)
		default:
			// 如果 filter 不是支持的类型，可以返回错误或者做适当的处理
			db.err = fmt.Errorf("filter类型仅支持bson.M或bson.D")
		}
	}

	option := options.Find()
	option.Limit = &db.start.Size

	option.Sort = bson.D{{Key: "_id", Value: db.start.Sort}}

	if db.fields != nil {
		option.Projection = db.fields
	}

	ctx1, cancel1 := db.context()
	defer cancel1()
	result, err := db.Collection.Find(ctx1, db.filter, option)
	if err != nil {
		db.err = err
		return db
	}

	db.cursor = result

	db.PrintLog("listStart")

	return db
}

/**
 * @Author: minglic
 * @description: 查询多条数据（只排序，不分页）
 * @return {*}
 */
func (db *MongoDB) List() *MongoDB {
	ctx, cancel := db.context()
	defer cancel()

	option := options.Find()
	if db.fields != nil {
		option.Projection = db.fields
	}
	if db.limit != nil {
		option.Limit = db.limit
	}
	if db.skip != nil {
		option.Skip = db.skip
	}

	if db.filter == nil {
		db.filter = bson.D{}
	}

	option.Sort = db.sort()

	result, err := db.Collection.Find(ctx, db.filter, option)
	if err != nil {
		db.err = err
		return db
	}
	db.cursor = result

	db.PrintLog("list")

	return db
}

/**
 * @Author: minglic
 * @description: 绑定数据到切片
 * @param {any} result
 * @return {*}
 */
func (db *MongoDB) All(model ...any) error {
	if db.err != nil {
		return db.err
	}

	var result any
	if len(model) > 0 {
		result = model[0]
	} else {
		result = db.model
	}

	defer db.cursor.Close(context.TODO())
	db.err = db.cursor.All(context.TODO(), result)
	return db.err
}

/**
 * @Author: minglic
 * @description: 绑定数据到切片
 * @param {any} result
 * @return {*}
 */
func (db *MongoDB) ResPager(model ...any) (*pagerx.ResPager, error) {
	err := db.All()
	if err != nil {
		return nil, err
	}

	if len(model) > 0 {
		db.model = model[0]
	}

	return &pagerx.ResPager{Pager: db.pager, Records: db.model}, err
}

func (db *MongoDB) ResStart(model ...any) (*pagerx.ResStart, error) {
	err := db.All()
	if err != nil {
		return nil, err
	}

	if len(model) > 0 {
		db.model = model[0]
	}

	return &pagerx.ResStart{Pager: db.start, Records: db.model}, err
}

/**
 * @Author: minglic
 * @description: 显示查询计划
 * @return {*}
 */
func (db *MongoDB) Explain() *MongoDB {

	// 如果未开启显示查询计划，则直接返回
	if !db.showLog {
		return db
	}

	// 构造 explain 命令
	explainCmd := bson.D{
		{Key: "explain", Value: bson.D{
			{Key: "find", Value: db.collection},
			{Key: "filter", Value: db.filter},
		}},
	}

	// 运行 explain 命令
	result := db.GetDatabase().RunCommand(context.TODO(), explainCmd)
	if result.Err() != nil {
		logs.DebugM(result.Err())
		return db
	}

	// 解析并打印结果
	var explainResult bson.M
	if err := result.Decode(&explainResult); err != nil {
		logs.DebugM(result.Err())
		return db
	}

	jsonStr := ToJson(explainResult)
	// logs.DebugM("完整的查询计划：")
	// logs.DebugM(jsonStr)
	logs.DebugM("===================查询计划分析=====================")
	json := gjson.Parse(jsonStr)
	logs.DebugM("查询指令:command", json.Get("command").String())
	logs.DebugF("扫描索引数:executionStats.totalKeysExamined = %d:", json.Get("executionStats.totalKeysExamined").Int())
	logs.DebugF("扫描文档数:executionStats.totalDocsExamined = %d:", json.Get("executionStats.totalDocsExamined").Int())
	logs.DebugF("引用索引名:queryPlanner.winningPlan.queryPlan.inputStage.indexName = %s", json.Get("queryPlanner.winningPlan.queryPlan.inputStage.indexName").String())

	return db
}

/**
 * @Author: minglic
 * @description: 更新单个文档
 * @param {bson.D} filter
 * @param {bson.D} update
 * @return {*}
 */
func (db *MongoDB) UpdateOne(update bson.M) (*mongo.UpdateResult, error) {
	ctx, cancel := db.context()
	defer cancel()

	db.update = update

	db.PrintLog("updateOne")

	return db.Collection.UpdateOne(ctx, db.filter, update)
}

/**
 * @Author: minglic
 * @description: 更新多条数据
 * @param {bson.D} filter
 * @param {bson.D} update
 * @return {*}
 */
func (db *MongoDB) UpdateMany(update bson.M) (*mongo.UpdateResult, error) {
	ctx, cancel := db.context()
	defer cancel()

	db.PrintLog("updateMany")

	if db.filter == nil {
		db.filter = bson.D{}
	}

	return db.Collection.UpdateMany(ctx, db.filter, update)
}

/**
 * @Author: minglic
 * @description: 替换全部字段
 * @param {any} model
 * @return {*}
 */
func (db *MongoDB) ReplaceOne(model ...any) (*mongo.UpdateResult, error) {

	ctx, cancel := db.context()
	defer cancel()

	if db.model == nil && len(model) > 0 {
		db.Model(model[0])
	} else if db.model == nil {
		return nil, fmt.Errorf("model is nil")
	}

	var replacement any
	if len(model) > 0 {
		replacement = model[0]
	} else {
		replacement = db.model
	}

	db.PrintLog("replaceOne")

	if db.filter == nil {
		return nil, fmt.Errorf("filter is nil")
	}

	return db.Collection.ReplaceOne(ctx, db.filter, replacement)
}

func (db *MongoDB) Save(model ...any) (any, error) {
	ctx, cancel := db.context()
	defer cancel()

	if db.model == nil {
		db.Model(model[0])
	}

	var replacement any
	if len(model) > 0 {
		replacement = model[0]
	} else {
		replacement = db.model
	}

	db.PrintLog("upsert")

	if db.filter == nil {
		//通过反射获取ID
		id, err := utils.GetFieldAny(replacement, "Model.ID")
		if err != nil {
			return nil, err
		}
		db.FilterID(id)
	}

	if db.filter == nil {
		return nil, fmt.Errorf("filter is nil")
	}
	result, err := db.Collection.ReplaceOne(ctx, db.filter, replacement)
	if err != nil {
		return nil, err
	}

	if result.MatchedCount > 0 {
		return result, nil
	}

	result2, err := db.Collection.InsertOne(ctx, replacement)
	if err != nil {
		return nil, err
	}

	return result2, nil
}

/**
 * @Author: minglic
 * @description: 删除单个文档
 * @return {*}
 */
func (db *MongoDB) DeleteOne() (*mongo.DeleteResult, error) {
	ctx, cancel := db.context()
	defer cancel()

	db.PrintLog("deleteOne")

	return db.Collection.DeleteOne(ctx, db.filter)
}

/**
 * @Author: minglic
 * @description: 删除单个文档
 * @return {*}
 */
func (db *MongoDB) DeleteObj(obj any) (*mongo.DeleteResult, error) {
	if db.model == nil {
		db.Model(obj)
	}
	id, err := utils.GetFieldAny(obj, "ID")
	if err != nil {
		return nil, err
	}
	return db.DeleteByID(id)
}

/**
 * @Author: minglic
 * @description: 删除多个文档
 * @return {*}
 */
func (db *MongoDB) DeleteMany() (*mongo.DeleteResult, error) {
	if db.filter == nil {
		db.filter = bson.D{}
	}
	ctx, cancel := db.context()
	defer cancel()
	if db.filter == nil {
		return nil, fmt.Errorf("filter is nil")
	}
	return db.Collection.DeleteMany(ctx, db.filter)
}

/**
 * @Author: minglic
 * @description: 指定ID删除
 * @return {*}
 */
func (db *MongoDB) DeleteByID(id any) (*mongo.DeleteResult, error) {

	db.PrintLog("deleteOne")

	return db.FilterID(id).DeleteOne()
}

/**
 * @Author: minglic
 * @description: 创建一条索引
 * @param {[]string} fields
 * @param {int} expireAfterSeconds
 * @param {bool} unique
 * @return {*}
 */
func (db *MongoDB) CreateIndexOne(fields []string, expireAfterSeconds int, unique bool) (string, error) {
	ctx, cancel := db.context()
	defer cancel()

	keys := bson.D{}
	name := ""
	for i, v := range fields {
		sort := 1
		if strings.HasPrefix(v, "-") {
			sort = -1
		}
		keys = append(keys, bson.E{Key: v, Value: sort})
		name += v + "_" + fmt.Sprintf("%d", sort)
		if i < len(fields)-1 {
			name += "_"
		}
	}

	imodel := mongo.IndexModel{}
	imodel.Keys = keys

	option := options.Index()
	option.SetUnique(unique)

	if expireAfterSeconds > 0 {
		option.SetExpireAfterSeconds(int32(expireAfterSeconds))
	}

	imodel.Options = option

	logs.DebugM("创建索引：", name)
	return db.Collection.Indexes().CreateOne(ctx, imodel)
}

/**
 * @Author: minglic
 * @description: 断开连接
 * @return {*}
 */
func (db *MongoDB) Disconnect() error {
	ctx, cancel := db.context()
	defer cancel()
	return db.Client.Disconnect(ctx)
}

/**
 * @Author: minglic
 * @description: 使用反射实现钩子，判断钩子函数是否存在，存在则调用
 * @param {any} obj
 * @param {string} methodName
 * @param {*mongox.MongoDB} db
 * @return {*}
 */
func (*MongoDB) CallMethodIfExists(obj any, methodName string, db *MongoDB) error {
	method := reflect.ValueOf(obj).MethodByName(methodName)
	if method.IsValid() && method.Kind() == reflect.Func {
		result := method.Call([]reflect.Value{reflect.ValueOf(db)})
		if len(result) > 0 && !result[0].IsNil() {
			// 方法返回了一个非 nil 的 error
			return result[0].Interface().(error)
		}
	}
	return nil
}

/**
 * @Author: minglic
 * @description: 指定ID获取一条数据
 * @param {any} id
 * @return {*}
 */
func (db *MongoDB) GetByID(id any) error {
	return db.FilterID(id).FindOne().One()
}

/**
 * @Author: minglic
 * @description: 获得一条数据
 * @param {any} model
 * @return {*}
 */
func (db *MongoDB) Get(model any) error {
	if db.model == nil {
		db.model = model
	}
	id, err := utils.GetFieldAny(model, "ID")
	if err != nil {
		return err
	}
	return db.FilterID(id).FindOne().One()
}
